package PrjHttpWs

/***
  使用JSON格式协议
*/

import (
	"dgo/goutils/utils"
	"gitee.com/ymofen/supervalue"
	"strings"
	"time"
)

/*
**

	发送支持多线程
*/
type WsHttpClt struct {
	objectDNA             uint64
	scribleChannelsMap    utils.GSyncMapGroup
	ws                    *WsClient
	RequestAttachInfoFlag int // 接收时有附加信息(请求)  (1:有, 0:不需要, -1:不发该请求)
	onRecvTopic           func(sender *WsHttpClt, topic string, data interface{})
	LogTypeStr            string
	OnConnectedFunc       func(sender *WsHttpClt)
}

func NewWsHttpClt(ws_req_url, origin_url string) *WsHttpClt {
	if len(origin_url) == 0 {
		protocol := "http://"
		tmpUrl := ws_req_url
		if strings.HasPrefix(tmpUrl, "ws://") {
			tmpUrl = strings.TrimPrefix(tmpUrl, "ws://")
		} else if strings.HasPrefix(tmpUrl, "wss://") {
			tmpUrl = strings.TrimPrefix(tmpUrl, "wss://")
			protocol = "https://"
		}
		strs := strings.SplitN(tmpUrl, "/", 2)
		origin_url = protocol + strs[0]
	}
	rval := &WsHttpClt{ws: NewWsClient(ws_req_url, origin_url)}
	rval.objectDNA = utils.NewObjectDNA64()
	rval.ws.OnConnect = rval.onWsConnected
	rval.ws.OnConnectErr = func(sender *WsClient, err error) {
		utils.Log.WarnTypef(rval.LogTypeStr, "[%d]连接服务器[%s]失败:%s", rval.objectDNA, ws_req_url, err.Error())
	}

	rval.ws.OnRecv = rval.onWsRecv
	return rval
}

func (this *WsHttpClt) GetWsUrl() string {
	return this.ws.ws_req_url
}

func (this *WsHttpClt) GetWsOrgUrl() string {
	return this.ws.origin_url
}

func (this *WsHttpClt) ConfigWsReqUrl(ws_req_url, origin_url string) bool {
	return this.ws.ConfigWsReqUrl(ws_req_url, origin_url)
}

func (this *WsHttpClt) ConfigWsOnConnectErr(evt func(sender *WsClient, err error)) {
	this.ws.OnConnectErr = evt
}

func (this *WsHttpClt) ConfigWsOnDis(evt func(sender *WsClient)) {
	this.ws.OnDis = evt
}

func (this *WsHttpClt) ConfigOnRecvTopic(onRecvTopic func(sender *WsHttpClt, topic string, data interface{})) {
	this.onRecvTopic = onRecvTopic
}

func (this *WsHttpClt) ConfigMonitor(monitor_send, monitor_recv *utils.MonitorSpeedSizeRec) {
	this.ws.Monitor_RecvSize = monitor_recv
	this.ws.Monitor_SendSize = monitor_send
}

func (this *WsHttpClt) IsConnected() bool {
	return this.ws != nil && this.ws.IsConnected()
}

func (this *WsHttpClt) GetTerminated() bool {
	return this.ws.terminated == 1
}

func (this *WsHttpClt) LastDisreason() string {
	return this.ws.dismsg
}

func (this *WsHttpClt) BreakConnect(reason string) {
	this.ws.RequestDisconnect(reason)
}

func (this *WsHttpClt) Start() {
	this.ws.CheckStartPingTask()
	this.ws.Start()
}

func (this *WsHttpClt) Stop() {
	this.ws.Stop()
}

func (this *WsHttpClt) WaitFor() {
	this.ws.WaitFor(time.Second * 10)
}

func (this *WsHttpClt) innerPublishMessage(topic string, data interface{}) error {
	if str, ok := data.(string); ok {
		if strings.Contains(str, WS_TOPIC_KEY) {
			return this.ws.SendMsg(WS_MESSAGE_TEXT, []byte(str))
		}

		if len(str) == 0 {
			return nil
		}

		l := len(str)
		if str[0] == '{' && str[l-1] == '}' && l > 3 { // JSON
			sb := utils.NewBytesBuilder()
			sb.AppendStr("{")
			sb.AppendStr("\"").AppendStr(utils.EscapeJsonStrSimple(WS_TOPIC_KEY)).AppendStr("\":")
			sb.AppendStr("\"").AppendStr(utils.EscapeJsonStrSimple(topic)).AppendStr("\", ")
			sb.AppendStr(str[1:])
			return this.ws.SendMsg(WS_MESSAGE_TEXT, sb.Bytes())
		}

		var jB utils.JSONBuilder
		jB.ObjectBegin()
		jB.AddEscapeStr(WS_TOPIC_KEY, topic).Spliter()
		jB.AddEscapeStr(WS_MSG_KEY, str)
		jB.ObjectEnd()
		return this.ws.SendMsg(WS_MESSAGE_TEXT, jB.Bytes())
	} else if json, ok := data.(*wrapper.SuperValue); ok {
		json.SetKeyValue(WS_TOPIC_KEY, topic)
		return this.ws.SendMsg(WS_MESSAGE_TEXT, json.AsJSONBytes(false))
	} else {
		json := wrapper.NewSVObject()
		json.SetKeyValues(WS_TOPIC_KEY, topic, WS_MSG_KEY, data)
		return this.ws.SendMsg(WS_MESSAGE_TEXT, json.AsJSONBytes(false))
	}
}

func (this *WsHttpClt) GetConnNum() int {
	if this.ws == nil {
		return 0
	}
	return this.ws.conn_n
}

/*
*

	支持多线程
*/
func (this *WsHttpClt) PublishMessage(topic string, data interface{}) error {
	return this.innerPublishMessage(topic, data)
}

func (this *WsHttpClt) onWsRecv(sender *WsClient, msgtype byte, msg []byte) {
	var recvstr string

	defer utils.PanicHandlerWithDebugString(recvstr)

	if this.onRecvTopic == nil {
		return
	}
	recvstr = utils.BufToHexStr(msg, 0, " ")
	if msgtype == WS_MESSAGE_TEXT {
		json, err := wrapper.NewSVFromBuf(msg)
		if err != nil {
			utils.Log.WarnTypef(this.LogTypeStr, "非法的数据:%s, 解析异常:%s", recvstr, err.Error())
			return
		}

		strTopic := json.StringByName(WS_TOPIC_KEY, "")
		if len(strTopic) == 0 {
			channels := json.StringByName("channels", "")
			if json.IntByName("code", 0) == 0 && len(channels) > 0 {
				utils.Log.InfoTypef(this.LogTypeStr, "[%d]订阅通过成功:%s", this.objectDNA, channels)
			} else {
				utils.Log.WarnTypef(this.LogTypeStr, "非法的数据:%s, 未指定TOPIC", recvstr)
			}

			return
		}

		val := json.VarByName(WS_MSG_KEY)
		if val != nil {
			this.onRecvTopic(this, strTopic, val)
		} else {
			json.RemoveKey(WS_TOPIC_KEY)
			this.onRecvTopic(this, strTopic, json)
		}

	}

}

func (this *WsHttpClt) GetSubscribleTopics() string {
	rval := ""
	this.scribleChannelsMap.Range(func(key, value interface{}) bool {
		rval += key.(string) + ","
		return true
	})
	return rval
}

func (this *WsHttpClt) innerSendSubScribleTopicCmd(topic string) {
	if this.RequestAttachInfoFlag != -1 {
		cmdstr := utils.BuildJSONString(WS_TOPIC_KEY, WS_HTTP_CHANNEL_CMD, "cmd", WS_HTTP_CMD_subscribe,
			"channels", topic, "request-attach", this.RequestAttachInfoFlag)
		this.ws.SendMsg(WS_MESSAGE_TEXT, []byte(cmdstr))
	} else {
		cmdstr := utils.BuildJSONString(WS_TOPIC_KEY, WS_HTTP_CHANNEL_CMD, "cmd", WS_HTTP_CMD_subscribe,
			"channels", topic)
		this.ws.SendMsg(WS_MESSAGE_TEXT, []byte(cmdstr))
	}
}

/*
*
用逗号分隔
{"__topic":"/cmd", "cmd":"subscribe", "channels":"", "request-attach":1}
*/
func (this *WsHttpClt) SubScribleTopic(topics string) {
	strs := strings.Split(topics, ",")
	for _, v := range strs {
		topic := utils.Trim(v)
		if len(topic) == 0 {
			continue
		}
		_, loaded := this.scribleChannelsMap.LoadOrStoreFunc2(topic, func() interface{} {
			return topic
		}, nil)
		if !loaded {
			if this.ws.IsConnected() {
				this.innerSendSubScribleTopicCmd(topic)
			}
		}
	}
}

/*
*
用逗号分隔
{"__topic":"/cmd", "cmd":"unsubscribe", "channels":""}
*/
func (this *WsHttpClt) UnsubScribleTopic(topics string) error {
	if this.ws.IsConnected() {
		cmdstr := utils.BuildJSONString(WS_TOPIC_KEY, WS_HTTP_CHANNEL_CMD, "cmd", WS_HTTP_CMD_unsubscribe,
			"channels", topics)
		err := this.ws.SendMsg(WS_MESSAGE_TEXT, []byte(cmdstr))
		if err != nil {
			return err
		}

	}

	strs := strings.Split(topics, ",")
	for _, v := range strs {
		topic := utils.Trim(v)
		if len(topic) == 0 {
			continue
		}
		this.scribleChannelsMap.Remove(topic)
	}
	return nil
}

func (this *WsHttpClt) onWsConnected(sender *WsClient) {
	channels := this.scribleChannelsMap.ValAsString2(",", nil, 0)
	if len(channels) > 0 {
		this.innerSendSubScribleTopicCmd(channels)
		//cmdstr := utils.BuildJSONString(WS_TOPIC_KEY, WS_HTTP_CHANNEL_CMD, "cmd", WS_HTTP_CMD_subscribe,
		//	"channels", channels)
		//this.ws.SendMsg(WS_MESSAGE_TEXT, []byte(cmdstr))
		utils.Log.InfoTypef(this.LogTypeStr, "[%d]连接服务器(%s)成功, 发送订阅请求:%s", this.objectDNA, sender.RemoteAddr(), channels)
	}
	if this.OnConnectedFunc != nil {
		this.OnConnectedFunc(this)
	}
}
